package snpi;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;

/**
 *
 * @author Trevor Brown
 */
public class SNPIDTUltraSparcT2Plus implements SNPI {
    private final static int ARRAY_OFFSET = 4;
    private final static int ARRAY_STRIDE = 8;
    
    private final AtomicLongArray hwthreadLocks = new AtomicLongArray(128 * ARRAY_STRIDE);
    private final AtomicLongArray coreLocks = new AtomicLongArray(8 * ARRAY_STRIDE);
    private final AtomicLong chipLocks = new AtomicLong();
    
    private volatile int counter = 0;
        
    public final int get() {
        return counter;
    }
    
    public final void increment(final int id) {
        final int result = counter;
        while (result == counter) {
            if (dtRequest(id)) {
                if (result == counter) {
                    counter = result+1;
                }
                dtRelease(id);
            }
        }
    }
    
    private boolean dtRequest(final int id) {
        if (hwthreadLocks.compareAndSet(ARRAY_OFFSET+ARRAY_STRIDE*(id/8), 0, 1)) {
            if (coreLocks.compareAndSet(ARRAY_OFFSET+ARRAY_STRIDE*(id/64), 0, 1)) {
                if (chipLocks.compareAndSet(0, 1)) {
                    return true;
                } else {
                    coreLocks.set(ARRAY_OFFSET+ARRAY_STRIDE*(id/64), 0);
                    hwthreadLocks.set(ARRAY_OFFSET+ARRAY_STRIDE*(id/8), 0);
                }
            } else {
                hwthreadLocks.set(ARRAY_OFFSET+ARRAY_STRIDE*(id/8), 0);
            }
        }
        return false;
    }
    
    private void dtRelease(final int id) {
        chipLocks.set(0);
        coreLocks.set(ARRAY_OFFSET+ARRAY_STRIDE*(id/64), 0);
        hwthreadLocks.set(ARRAY_OFFSET+ARRAY_STRIDE*(id/8), 0);
    }
    
}
